#ifndef ENVIRONMENT_HPP
#define ENVIRONMENT_HPP

#include "Specimen.hpp"
#include "EvalFunction.hpp"
#include <vector>
#include <ostream>

/*!
 * \brief Base class modelling the environment in which the specimens evolve
 *
 * Class enables numeric optimization of target function using evolution 
 * mechanisms
 */
 
class Environment
{
  ///\brief This enumeration helps to check if environment is set up to go
  typedef enum eEFlag{
    DIMS_SET=(1<<0),
    MAX_ITER_SET=(1<<1),
    MAX_POPULATION_SET=(1<<2),
    MUT_PROBAB_SET=(1<<3),
    CROSS_PROP_SET=(1<<4),
    CROSS_PROBAB_SET=(1<<5),
    MAX_X_SET=(1<<6),
    MIN_X_SET=(1<<7),
    EPS_X_SET=(1<<8),
    EPS_Y_SET=(1<<9),
    OUT_STREAM_SET=(1<<10)
  }EnvironmentFalgs;


protected:
  long int _flags;
  /*!
   * \brief Vector of parent specimens 
   *
   * Vector of specimens used as source of genes for crossover and mutation 
   * operations
   */ 
  std::vector<Specimen> _parents;
  /*!
   * \brief Vector of ofspring specimens 
   *
   * Vector of specimens created as result of crossover and mutation operations
   */
  std::vector<Specimen> _children;
  ///\brief Smallest distance between specimens from consecutive generations
  Gene _epsilonX; 
  ///\brief Smallest difference between fitness scores between consecutive 
  /// generations
  Gene _epsilonY;
  long _maxIterations; ///< Maximum number of generations to simulate
  long _iteration; ///< Number of current generation 
  long _maxPopulation; ///< Maximum number of specimens
  unsigned _dimensionality; ///< Number of dimensions of problem
  double _mutationProbability;///<Probability of mutation
  double _crossoverProbability;///<Probability of being chosen to mating pool
  double _crossoverProportion;///<Proportion of algebraic crossover
  ///\brief Vector containing min possible values for each coordinate
  std::vector<Gene> _minX;
  ///\brief Vector containing max possible values for each coordinate
  std::vector<Gene> _maxX;
  ///\brief Function used for evaluation of specimens fitness
  //double (*_evaluationFunction)(Specimen &);
  // EvalFunction *_evaluationFunction;
  std::string _environmentName;
  /*!
   *  Method used for selection of specimens which shall be parents of new
   * generation of specimens
   */
  virtual void selection()=0;
  
  ///\brief Internal method for setting environment name
  void setEnvironmentName(const std::string &name){_environmentName=name;};

  ///\brief Method evaluating all children specimens.
  void evaluation(EvalFunction &evalFunction);
  ///\brief Method recombinating selected parents
  void recombination();

  ///\brief Method for making one evaluation-selection-evolution step with 
  /// output
  void makeGeneration(EvalFunction &evalFunction,std::ostream &stream);

public:
  ///\brief Default constructor
  Environment():_flags(0),_epsilonX(0),_epsilonY(0),_maxIterations(0),
		_iteration(0),_maxPopulation(0),_dimensionality(0),
		_mutationProbability(0),_crossoverProportion(0),
		_environmentName("base"){};
  ///\brief Destructor
  virtual ~Environment();
  
  ///\brief Setter method for maximum number of specimens in population
  void setMaxPopulation(unsigned maxPopulation);
  ///\brief Setter method for fitness evaluation function
  //  void setEvaluationFunction(EvalFunction *function);
  ///\brief Method for making one evaluation-selection-evolution step
  void makeGeneration(EvalFunction &evalFunction);
  ///\brief Setter method for fitness change stop condition
  void setFitnessMinChange(const Gene& epsilonY);
  ///\brief Setter method for distance between specimens stop condition
  void setCoordinatesMinChange(const Gene &epsilonX);
  ///\brief Setter function for number of iterations stop condition
  void setMaxIterations(const long& interations);
  ///\brief Setter function for mutation probability
  void setMutationProbability(const double &probability);
  ///\brief Setter function for crossover probability
  void setCrossoverProbability(const double &probability);
  ///\brief Setter function for algebraic mutation proportion
  void setAlgebraicCrossoverProportion(const double &proportion);
  ///\brief Setter function for higher bounds of problem domain 
  void setMaxX(const std::vector<Gene> &maxX);
  ///\brief Setter function for lower bounds of problem domain
  void setMinX(const std::vector<Gene> &minX);
  ///\brief Setter function for number of problem domain dimensions
  void setDimensionality(const unsigned &dims);
  
  /*!
   * \brief Method checking if all parameters are prepared and generating the
   * first generation of specimens
   */
  void prepare(EvalFunction &evalFunction);

  ///\brief Method returning vector with newest generation
  std::vector<Specimen> getChildren();
  
  ///\brief Method preforming the search for optimum
  Specimen search(EvalFunction &evalFunction);

  ///\brief Method preforming the search for optimum with output stream
  Specimen search(EvalFunction &evalFunction,std::ostream &stream);

  ///\brief Method reinintialising the environment
  void reboot();

};

#endif
